//======================================================================
//文件名称：tm1637_led_kb.c
//功能概要：TM1637_LED_KB软件构件源文件
//技术参考：TM1637芯片数据手册
//版权所有：江苏电子-索明何
//版本更新：2023-03-13  V1.0
//======================================================================
//1.包含本软件构件头文件
#include  "tm1637_led_kb.h"  

//2.仅用于本文件的宏定义
//TM1637控制数码管显示亮度宏定义
#define  DISP_LUM    (0x8F)            //0x88~0x8F对应的亮度加大，0x80为关显示

//3.仅用于本文件的全局变量和内部函数的声明
//内部函数声明
static void  tm1637_start(void);             //使TM1637产生起始信号
static void  tm1637_write(uint8 data);       //向TM1637串行写入1字节数据
static void  tm1637_wait_ack(void);          //等待TM1637产生应答信号
static void  tm1637_stop(void);              //使TM1637产生停止信号
static uint8  kb_key_name(uint8 key_value);  //将键值解析出对应的键名

//4.对外接口函数的定义与实现
//======================================================================
//函数名称：tm1637_init
//函数功能：将TM1637的CLK和DIO引脚对应的MCU引脚均设置为GPIO输出功能
//函数参数：无
//函数返回：无
//======================================================================
void tm1637_init(void)
{
    gpio_init(TM1637_CLK, GPIO_OUT, 1);  
    gpio_init(TM1637_DIO, GPIO_OUT, 1);  
}

//======================================================================
//函数名称：tm1637_leds_display
//函数功能：TM1637驱动数码管动态显示（最多6位数码管）
//函数参数：*buff: 用于指向数码管显示数据缓冲区的首地址（可用实参数组名作为函数实参）
//函数返回：无
//======================================================================
void tm1637_leds_display(uint8 *buff)	
{
    //1.定义局部变量	
    uint8  i;
    uint8  tableNo;           //存放待显示字符在数码管笔形码数组中对应的下标
	
    //2.依次产生起始信号、写入命令、等待应答信号、产生停止信号	
    tm1637_start();           //产生起始信号
    tm1637_write(0x40);       //写入命令（向显示寄存器写入数据，采用自动地址加1模式）
    tm1637_wait_ack();        //等待应答信号
    tm1637_stop();            //产生停止信号
	
    //3.依次产生起始信号、写入命令、等待应答信号	
    tm1637_start();           //产生起始信号
    tm1637_write(0xC0);       //设置显示首地址（对应左起第1位数码管）
    tm1637_wait_ack();        //等待应答信号
	
    //4.向显示寄存器写入所有待显示字符对应的笔形码
    for(i=0; i<LED_NUM; i++)  
    {
        tableNo = buff[i];              
        tm1637_write(LED_table[tableNo]);   //写入1个字符对应的笔形码
        tm1637_wait_ack();                  //等待应答信号
    }
    tm1637_stop();                          //产生停止信号
		
    //5.依次产生起始信号、写入命令、等待应答信号、产生停止信号		
    tm1637_start();                         //产生起始信号
    tm1637_write(DISP_LUM);                 //写入显示控制命令（亮度控制）
    tm1637_wait_ack();                      //等待应答信号
    tm1637_stop();                          //产生停止信号	
}

//======================================================================
//函数名称：tm1637_leds_clear
//函数功能：TM1637控制数码管清屏
//函数参数：无
//函数返回：无
//======================================================================
void tm1637_leds_clear(void)	
{	
    uint8  i, disp[LED_NUM];
    for(i=0; i<LED_NUM; i++)
    {
        disp[i] = 29;            //将数码管显示数据缓冲区设为全灭对应的下标
    }
    tm1637_leds_display(disp);   //调用数码管显示函数
}

//======================================================================
//函数名称：led_buff_update
//函数功能：更新数码管显示数据缓冲区（对应笔形码数组中的下标）
//函数参数：dispdata:  在多位数码管上显示的数据（整数）
//          led_buff[ ]: 用于接收数码管显示数据缓冲区的首地址（可用实参数组名作为函数实参）
//函数返回：无
//相关说明：若控制多位数码管显示整数1234，则可先通过调用该函数，使数码管显示数据缓冲区
//          （数组）中的多个数据更新为数字1、2、3、4对应的笔形码数组下标1、2、3、4
//======================================================================
void led_buff_update(uint32 dispdata, uint8 led_buff[ ])
{
    uint8 i;
    for(i=0; i<LED_NUM; i++)
    {
        led_buff[LED_NUM-1-i] = dispdata%10;
        dispdata = dispdata/10;
    }	
}

//======================================================================
//函数名称：tm1637_kb_scan
//函数功能：TM1637扫描矩阵式键盘
//函数参数：无
//函数返回：若有键被按下，则返回被按下键对应的键名（'1'、'2'、'3'、...），否则返回0xFF
//======================================================================
uint8 tm1637_kb_scan(void)
{
    //1.定义局部变量	
    uint8  i;
    uint8  key_value = 0;
    uint8  key_name = 0xFF;
	
    //2.依次产生起始信号、写入读键命令、等待应答信号	
    tm1637_start();                         //产生起始信号
    tm1637_write(0x42);                     //写入读键命令
    tm1637_wait_ack();                      //等待应答信号
	
    //3.先将DIO拉高，然后将其对应的MCU引脚设为输入功能	
    gpio_set(TM1637_DIO, 1);                //将DIO拉高
    __ASM("NOP"); __ASM("NOP");             //空操作	
    gpio_init(TM1637_DIO, GPIO_IN, 1);      //将DIO对应的MCU引脚设为输入
    gpio_pull(TM1637_DIO, PULL_UP);         //使能DIO对应的MCU引脚内部上拉
	
    //4.依次读取DIO上的8位数据（低位在前），获取键值
    for(i=0; i<8; i++)                 
    {
        gpio_set(TM1637_CLK, 0);            //使CLK为低电平
        Delay_us(40);                       //延时40us			
        gpio_set(TM1637_CLK, 1);	          //使CLK为高电平				
        key_value |= gpio_get(TM1637_DIO) <<(7-i);			
        Delay_us(40);                       //延时40us
    }
		
    //5.读完键值后，将DIO对应的MCU引脚恢复为输出功能
    gpio_init(TM1637_DIO, GPIO_OUT, 1);
    __ASM("NOP");  __ASM("NOP");            //空操作
		
    //6.等待应答信号、产生停止信号
    tm1637_wait_ack();                      //等待应答信号
    tm1637_stop();                          //产生停止信号
		
    //7.若有键被按下，则解析出被按下键的键名
    if(key_value != 0xff) 
        key_name = kb_key_name(key_value);
		
    //8.返回键名（若无键被按下，则返回0xFF）
    return  key_name;			
}

//5. 内部函数的定义与实现
//======================================================================
//函数名称：tm1637_start
//函数功能：使TM1637产生起始信号，做好向TM1637写入数据的准备
//函数参数：无
//函数返回：无
//相关说明：在CLK引脚为高电平时，DIO引脚由高电平变为低电平，将产生起始信号
//======================================================================
static void tm1637_start(void)
{
    gpio_set(TM1637_CLK, 1);              //使CLK为高电平
    gpio_set(TM1637_DIO, 1);              //使DIO为高电平	
    Delay_us(2);	                        //延时2us
    gpio_set(TM1637_DIO, 0);              //使DIO变为低电平
}
//======================================================================
//函数名称：tm1637_write
//函数功能：向TM1637串行写入1字节数据
//函数参数：无
//函数返回：无
//相关说明：在CLK为低电平时，向DIO串行写入1位数据（从字节数据的低位开始），
//          写入1位数据后，需将CLK变为高电平且DIO上的信号保持不变
//======================================================================
static void tm1637_write(uint8 data)
{
    uint8  i;
    for(i=0; i<8; i++)
    {
        gpio_set(TM1637_CLK, 0);            //使CLK为低电平
        gpio_set(TM1637_DIO, data & 0x01);  //向DIO写入1位数据
        Delay_us(3);                        //延时3us
        data = data>>1;                     //将字节数据右移1位
        gpio_set(TM1637_CLK, 1);            //使CLK为高电平
        Delay_us(3);                        //延时3us			
    }    
}
//======================================================================
//函数名称：tm1637_wait_ack
//函数功能：等待TM1637产生应答信号
//函数参数：无
//函数返回：无
//相关说明：当数据传输正确时，会在第8个时钟的下降沿，芯片内部自动产生应答信号，
//          将DIO引脚拉低
//======================================================================
static void tm1637_wait_ack(void)
{
    gpio_set(TM1637_CLK, 0);              //使CLK为低电平
    Delay_us(5);	                        //延时5us，等待应答信号
    gpio_set(TM1637_CLK, 1);              //使CLK为高电平
    Delay_us(2);	                        //延时2us
    gpio_set(TM1637_CLK, 0);              //使CLK为低电平	
}

//======================================================================
//函数名称：tm1637_stop
//函数功能：使TM1637产生停止信号
//函数参数：无
//函数返回：无
//相关说明：当CLK为高电平时，DIO由低电平变为高电平，将产生停止信号
//======================================================================
static void tm1637_stop(void)
{
    gpio_set(TM1637_CLK, 0);              //使CLK为低电平
    gpio_set(TM1637_DIO, 0);              //使DIO为低电平	
    Delay_us(2);	                        //延时2us
    gpio_set(TM1637_CLK, 1);              //使CLK为高电平
    Delay_us(2);	                        //延时2us
    gpio_set(TM1637_DIO, 1);              //使DIO为高电平
}
//======================================================================
//函数名称：kb_key_name
//函数功能：根据键值解析出对应的键名
//函数参数：key_value: 键值
//函数返回：键值对应的键名（'1'、'2'、'3'、...）
//======================================================================
static uint8 kb_key_name(uint8 key_value)
{
    uint8  key_name = 0xFF;             //存放键名
    uint8  i = 0;	
	
    //在键值与键名表中搜索键值对应的键名，直到表尾
    while(kb_table[i] != 0xFF)  
    {
        if(kb_table[i] == key_value)    //在表中找到相应的键值
        {
            key_name = kb_table[i+1];   //获取对应的键名
            break;                      //终止搜素
        }	
        i += 2;                         //指向下一个键值，继续搜索				
    }	
		
    //返回被按下键对应的键名
    return  key_name;
}
